Keep images out of rich text fields

Best practice Recommended
Images belong in image fields on the document (or folder) that shows them, rendered by the template with linkImage/linkFile – not as <img> tags inside a rich text field pointing at a folder of image files. An image in rich text shows as broken in the Docly editor, cannot be replaced by the editor, has no scaling, format or alt-text control, and drags helper folders into the content tree.
Applies to: SchemasImagesHash templates

What you'll see

Pages have images in the middle of their text: a heading drawn as an image, a photo floated to the right of a paragraph. When the content comes from somewhere else – a converted site, a pasted article – the images arrive as <img src="/Images/photo.jpg"> inside the HTML, and the files go into a folder next to the pages so the paths resolve. The published page looks right.

Then the customer opens the document in the Docly editor and every one of those images is a broken-image icon.

What's actually happening

The rich text editor renders the HTML on the Docly portal's own address, not on the published site. A root-relative src="/Images/photo.jpg" is resolved against the portal and returns 404 there, while the published page resolves it against the site and finds the file. Observed on a converted site: every heading image and photo in rich text was broken in the editor and correct on the site.

That is the visible symptom. The rest is structural:

  • The rich text editor in a designer schema has no image tool, so the editor cannot replace or add such an image – only delete it by accident.
  • The file lives in a helper folder in the content tree, not with the page. Moving or deleting the page leaves it behind, and the customer sees a folder that is not a page.
  • The template cannot scale it, serve it as WebP/AVIF, register its size or give it an SEO filename – linkImage only works on files it knows about.
  • The alt text is whatever was pasted, usually empty.

An image field (ObjectVariant: "Camera") stores the file on the document itself (EmbeddedFiles), the editor shows a thumbnail with upload and replace, and the template links it with docly.linkImage / docly.linkFile. The same works on a folder schema – the file is then served from the folder's address.

What to do

Do – one field per image the layout has, rendered by the template:

  • A heading drawn as an image: a text field Heading and an image field HeadingImage. Render <h1><img src="#docly.linkFile(HeadingImage)#" alt="#docly.htmlAttributeEncode(Heading)#"></h1> when there is an image, otherwise <h1> with the text. The page gets a real heading and the image keeps its look.
  • A photo inside the text: an image field plus the fields that say where it goes – which text field and which paragraph (at the start of paragraph n), and whether it has margin. The template inserts it; the text stays pure text:
    let n = 0;
    let html = String(Text || "").replace(/<p(s[^>]*)?>/gi,
        m => ++n == Number(ImageParagraph) ? m + img : m);
  • An alt text field next to every content image, filled in.
  • Scaled and modern formats through linkImage with registered sizes – see Restrict and register image sizes and Use AVIF and WebP image formats.

Don't: <img> in a rich text field, and a folder of image files in the content tree to make its paths resolve.

Converting content: lift every <img> out of the imported HTML into the matching field (with its position), embed the file in the document, and delete the helper folder. When the old site displayed an image at a size other than its natural one, scale the file to the displayed size on import so it needs no CSS size. Then open a document in the editor: no broken images, and the images appear as fields.